// $Id: colshapemanager.h 470 2008-05-27 16:27:22Z agjermun $
// declaration of the ColShapeManager class designed to store and manage collision shapes
//
// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#ifndef __COLSHAPEMANAGER_H
#define __COLSHAPEMANAGER_H

#include "Ogre.h"
#include "OgreSingleton.h"
#include <btBulletCollisionCommon.h>
#include <map>

#include "less.h"

using namespace std;
using namespace Ogre;

/**
 * Manager which simplifies creation and reuse of collision shapes for the physics engine.
 * Holds a reference counted link to all collision shapes in use, allows sharing between different objects using
 * the ColShapeKey class.
*/
class ColShapeManager : public Singleton<ColShapeManager>
{
public:
        /** enum which contains different type of collision shapes */
	enum shapeType { 
		box, cylinder, 
		cone, capsule, 
		sphere, 
		triangle, 
		//convexhull, bhvtrianglemesh, trianglemesh, 
		collada         /**< bases the shape on a loaded COLLADA-file */
	};

	/**
	 * Provides a simple identifier for collision shapes to use with the ColShapeManager.
	 * Can be used to check if the shape is already generated and thus allow reuse, or to
	 * generate a new shape based on the information in the ColShapeKey object.
	*/
	class ColShapeKey
	{
	private:
                /** compare function for Bullet vectors,
                 *  @param l first vector to compare (left)
                 *  @param r second vector to compare (right)
                 *  @return true if left's x,y or z-coordinate is less than the right's
                */
		inline bool compareV3(const btVector3 &l, const btVector3 &r) const
		{
			return((l.x() < r.x()) ||
					(l.y() < r.y()) ||
					(l.z() < r.z()));
		}

	public:
                /// stores the key size data for the collision shape
		union Datatypes {
			btVector3 *halfExtents;
			btScalar *radius;
			btScalar *height;
			btVector3 *points;
			String *colladaFile;
		} mData;

		shapeType mShapeType; /**< type of shape */

		//ColShapeKey();
		ColShapeKey(shapeType s, Vector3 half); // box, cylinder
		ColShapeKey(shapeType s, btScalar radius, btScalar height); // Cone, capsule
		ColShapeKey(shapeType s, Vector3 p[3]); // Triangle
		ColShapeKey(shapeType s, btScalar radius); // Sphere
		ColShapeKey(shapeType s, String colladaFile); // collada

		ColShapeKey(const ColShapeKey &key); // copy-constructor
		// TODO: trimesh, convexhull
		~ColShapeKey();

                /** 
                 * overloaded less-than operator 
                 * @param right object to compare with
                 * @return true if the left object is smaller than the right
                */
		bool operator< (const ColShapeKey &right) const
		{
			if(this->mShapeType != right.mShapeType)
				return(this->mShapeType < right.mShapeType);
			switch(this->mShapeType)
			{
				case box:
					// drop through
				case cylinder:
					return(compareV3(*this->mData.halfExtents, *right.mData.halfExtents));
					break;

				case cone:
					// drop through
				case capsule:
					return((this->mData.radius < right.mData.radius) ||
							(this->mData.height < right.mData.height));
					break;

				case sphere:
					return(this->mData.radius < right.mData.radius);
					break;

				case triangle:
					return(compareV3(this->mData.points[0], right.mData.points[0]) ||
							compareV3(this->mData.points[1], right.mData.points[1]) ||
							compareV3(this->mData.points[2], right.mData.points[2]));
					break;

				case collada:
					return(this->mData.colladaFile < right.mData.colladaFile);
					break;
			}
			return(false); // default
		}
	};

	ColShapeManager();
	~ColShapeManager();
	
	bool shapeExists(ColShapeKey &key);
	btCollisionShape *getShape(ColShapeKey key);
	void removeShape(ColShapeKey key);

	static ColShapeManager& getSingleton(void);
	static ColShapeManager* getSingletonPtr(void);


private:
        /** contains all collision shapes for the singleton */
	map<ColShapeKey, pair<unsigned int, btCollisionShape*>, Less<ColShapeKey, ColShapeKey> > collisionShapes;
};

#endif //#ifndef __COLSHAPEMANAGER_H
